/*
异步编程对 JavaScript 语言太重要。
JavaScript 语言的执行环境是“单线程”的，如果没有异步编程，根本没法用，非卡死不可。
本章主要介绍 Generator 函数如何完成异步操作。


ES6 诞生以前，异步编程的方法，大概有下面四种。

    回调函数
    事件监听 (我也是异步编程！)
    发布/订阅
    Promise 对象	

Generator 函数将 JavaScript 异步编程带入了一个全新的阶段。




异步

所谓"异步"，简单说就是一个任务不是连续完成的，可以理解成该任务被人为分成两段，先执行第一段，然后转而执行其他任务，
等做好了准备，再回过头执行第二段。

比如，有一个任务是读取文件进行处理，任务的第一段是向操作系统发出请求，要求读取文件。
然后，程序执行其他任务，等到操作系统返回文件，再接着执行任务的第二段（处理文件）。
这种不连续的执行，就叫做异步。

相应地，连续的执行就叫做同步。由于是连续执行，不能插入其他任务，所以操作系统从硬盘读取文件的这段时间，程序只能干等着。



回调函数

JavaScript 语言对异步编程的实现，就是回调函数。所谓回调函数，就是把任务的第二段单独写在一个函数里面，
等到重新执行这个任务的时候，就直接调用这个函数。回调函数的英语名字callback，直译过来就是"重新调用"。

读取文件进行处理，是这样写的。
*/
fs.readFile('/etc/passwd', 'utf-8', function(err, data) {
	if (err) throw err;
	console.log(data);
});
/*
上面代码中，readFile函数的第三个参数，就是回调函数，也就是任务的第二段。
等到操作系统返回了/etc/passwd这个文件以后，回调函数才会执行。

一个有趣的问题是，为什么 Node 约定，回调函数的第一个参数，必须是错误对象err（如果没有错误，该参数就是null）？

原因是执行分成两段，第一段执行完以后，任务所在的上下文环境就已经结束了。
在这以后抛出的错误，原来的上下文环境已经无法捕捉，只能当作参数，传入第二段。
*/




/*
回调函数本身并没有问题，它的问题出现在多个回调函数嵌套。
假定读取A文件之后，再读取B文件，代码如下。
*/
fs.readFile(fileA, 'utf-8', function(err, data) {
	fs.readFile(fileB, 'utf-8', function(err, data) {
		// ...
	});
});
/*
不难想象，如果依次读取两个以上的文件，就会出现多重嵌套。代码不是纵向发展，而是横向发展，很快就会乱成一团，
无法管理。因为多个异步操作形成了强耦合，只要有一个操作需要修改，它的上层回调函数和下层回调函数，
可能都要跟着修改。这种情况就称为"回调函数地狱"（callback hell）。

Promise 对象就是为了解决这个问题而提出的。它不是新的语法功能，而是一种新的写法，允许将回调函数的嵌套，
改成链式调用。采用 Promise，连续读取多个文件，写法如下。
*/
var readFile = require('fs-readfile-promise');

readFile(fileA)
	.then(function(data) {
		console.log(data.toString());
	})
	.then(function() {
		return readFile(fileB); //这个是promise对象
	})
	.then(function(data) {
		console.log(data.toString());
	})
	.catch(function(err) {
		console.log(err);
	});
/*
上面代码中，我使用了fs-readfile-promise模块，它的作用就是返回一个 Promise 版本的readFile函数。
Promise 提供then方法加载回调函数，catch方法捕捉执行过程中抛出的错误。

可以看到，Promise 的写法只是回调函数的改进，使用then方法以后，异步任务的两段执行看得更清楚了，
除此以外，并无新意。

Promise 的最大问题是代码冗余，原来的任务被 Promise 包装了一下，不管什么操作，一眼看去都是一堆then，
原来的语义变得很不清楚。

那么，有没有更好的写法呢？
*/
